﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Security.Principal;
using Microsoft.IdentityModel.Claims;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
//using System.Threading.Tasks.Dataflow;


namespace ClaimsScaffold.Claims
{
    /// <summary>
    /// A claim provider that augments claims.
    /// </summary>
    public class ClaimProvider : SPActiveDirectoryClaimProvider
    {
        //THIS CONSTANT DEFINES THE CLAIM VALUE THAT WILL BE ASSIGNED TO TEST USERS DURING THE AUTH EVENT.
        //CHANGE THE '~' DELIMITER TO '|' TO SEE SEARCH FAIL TO RETURN RESULTS (AFTER RUNNING A CRAWL).
        public const string TEST_CLAIM_VALUE = "contoso~arbitraryValue1~arbitraryValue2";


        /// <summary>
        /// The description for the claim provider.
        /// </summary>
        public const string PROVIDER_DESCRIPTION = "Claims Scaffold";

        /// <summary>
        /// The original issuer for the claims.
        /// </summary>
        public const string ORIGINAL_ISSUER = "claimsscaffold";

        /// <summary>
        /// The encoded string identifying the original issuer
        /// </summary>
        public readonly static string ORIGINAL_ISSUER_STRING = SPOriginalIssuers.Format(SPOriginalIssuerType.ClaimProvider, ClaimProvider.ORIGINAL_ISSUER);

        /// <summary>
        /// 
        /// </summary>
        public const string CLAIM_TYPE = "http://schemas.claimsscaffold.com/claimsscaffold/2015/claims/testClaim";



        /// <summary>
        /// The name of the claim provider.
        /// </summary>
        public const string PROVIDER_NAME = "Claims Scaffold";


        /// The class constructor.
        /// </summary>
        /// <param name="displayName">The display name for the provider.</param>
        public ClaimProvider(string displayName)
            : base(displayName)
        {
        }

        static ClaimProvider()
        { }

        /// <summary>
        /// An accessor for the provider name.
        /// </summary>
        public override string Name
        {
            get
            {
                return ClaimProvider.PROVIDER_NAME;
            }
        }

        /// <summary>
        /// Indicates whether or not entity information is provided.
        /// </summary>
        public override bool SupportsEntityInformation
        {
            get
            {
                return true;
            }
        }

        public override bool SupportsResolve
        {
            get
            {
                return true;
            }
        }

        public override bool SupportsSearch
        {
            get
            {
                return true;
            }
        }


        protected override void FillClaimsForEntity(Uri context, SPClaim entity, List<SPClaim> claims)
        {

            try
            {

                SPSecurity.RunWithElevatedPrivileges(() =>
                {
                    if (null != claims && null != entity && SPClaimProviderManager.IsUserIdentifierClaim(entity))
                    {                       

                        SPClaim userClaim = ClaimProvider.CreateClaim(ClaimProvider.CLAIM_TYPE, TEST_CLAIM_VALUE);// String.Format("{0}{3}{1}{3}{2}", new object[] { domain, "arbitraryValue1", "arbitraryValue2", CLAIM_DELIMITER }).ToLower());
                        claims.Add(userClaim);
                    }

                });

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

        }


        protected override void FillClaimTypes(List<string> claimTypes)
        {
            base.FillClaimTypes(claimTypes);

            if (null != claimTypes)
            {
                claimTypes.Add(ClaimProvider.CLAIM_TYPE);
            }
        }

        protected override void FillClaimValueTypes(List<string> claimValueTypes)
        {
            base.FillClaimValueTypes(claimValueTypes);

            if (null != claimValueTypes)
            {
                claimValueTypes.Add(Microsoft.IdentityModel.Claims.ClaimValueTypes.String);
            }
        }

        protected override void FillEntityTypes(List<string> entityTypes)
        {
            base.FillEntityTypes(entityTypes);

            if (null != entityTypes)
            {
                entityTypes.Add(SPClaimEntityTypes.FormsRole);
            }
        }



        protected override void FillHierarchy(Uri context, string[] entityTypes, string hierarchyNodeID, int numberOfLevels, SPProviderHierarchyTree hierarchy)
        {
            Debug.WriteLine("FillHierarchy called");
            base.FillHierarchy(context, entityTypes, hierarchyNodeID, numberOfLevels, hierarchy);
        }

        protected override void FillSearch(Uri context, string[] entityTypes, string searchPattern, string hierarchyNodeID, int maxCount, SPProviderHierarchyTree searchTree)
        {
            Debug.WriteLine("FillSearch called");
            base.FillSearch(context, entityTypes, searchPattern, hierarchyNodeID, maxCount, searchTree);
        }

        protected override void FillResolve(Uri context, string[] entityTypes, SPClaim resolveInput, List<PickerEntity> resolved)
        {
            Debug.WriteLine("FillResolve(Uri context, string[] entityTypes, SPClaim resolveInput, List<PickerEntity> resolved) called");
            base.FillResolve(context, entityTypes, resolveInput, resolved);

        }


        protected override void FillResolve(Uri context, string[] entityTypes, string resolveInput, List<PickerEntity> resolved)
        {
            Debug.WriteLine("FillResolve(Uri context, string[] entityTypes, string resolveInput, List<PickerEntity> resolved) called");
            base.FillResolve(context, entityTypes, resolveInput, resolved);
        }


        /// <summary>
        /// A means to create a claim.
        /// </summary>
        /// <param name="claim">The claim to create.</param>
        /// <param name="claimValue">The associated claim value.</param>
        /// <returns>A claim having the associated value.</returns>
        public static SPClaim CreateClaim(string claim, string claimValue)
        {
            SPClaim result = new SPClaim(claim, claimValue.ToLower(), Microsoft.IdentityModel.Claims.ClaimValueTypes.String, ClaimProvider.ORIGINAL_ISSUER_STRING);
            return result;
        }


        /// <summary>
        /// Indicates whether or not a given principal has a particular claim value for a claim type.
        /// </summary>
        /// <param name="principal">The principal to test.</param>
        /// <param name="claimType">The claim type of interest.</param>
        /// <param name="claimValue">The claim value of interest.</param>
        /// <returns>A boolean indicating whether or not a principal has an associated claim.</returns>
        public static bool HasClaim(IPrincipal principal, string claimType, string claimValue)
        {
            bool result = false;

            if (null != principal)
            {
                result = ClaimProvider.HasClaim(principal as IClaimsPrincipal, claimType, claimValue);
            }

            return result;
        }

        /// <summary>
        /// Indicates whether or not a given principal has a particular claim value for a claim type.
        /// </summary>
        /// <param name="principal">The principal to test.</param>
        /// <param name="claimType">The claim type of interest.</param>
        /// <param name="claimValue">The claim value of interest.</param>
        /// <returns>A boolean indicating whether or not a principal has an associated claim.</returns>
        public static bool HasClaim(IClaimsPrincipal principal, string claimType, string claimValue)
        {
            bool result = false;

            if (null != principal)
            {
                result = ClaimProvider.HasClaim(principal.Identity as IClaimsIdentity, claimType, claimValue);
            }

            return result;
        }


        /// <summary>
        /// Indicates whether or not a given identity has a particular claim value for a claim type.
        /// </summary>
        /// <param name="identity">The claims identity to test.</param>
        /// <param name="claimType">The claim type of interest.</param>
        /// <param name="claimValue">The claim value of interest.</param>
        /// <returns>A boolean indicating whether or not the identity has an associated claim.</returns>
        public static bool HasClaim(IClaimsIdentity identity, string claimType, string claimValue)
        {
            bool result = false;

            if (null != identity && !String.IsNullOrEmpty(claimType) && !String.IsNullOrEmpty(claimValue))
            {
                result = identity.Claims.Exists((c) =>
                {
                    return (c.ClaimType.Equals(claimType, StringComparison.OrdinalIgnoreCase) &&
                        c.OriginalIssuer.Equals(ClaimProvider.ORIGINAL_ISSUER_STRING, StringComparison.OrdinalIgnoreCase) &&
                        c.Value.Equals(claimValue, StringComparison.OrdinalIgnoreCase));
                }
                );
            }

            return result;
        }

        /// <summary>
        /// Indicates whether or not a given principal has a particular claim value for a claim type.
        /// </summary>
        /// <param name="principal">The principal to test.</param>
        /// <param name="issuer">Identifies the original issuer of the claim.</param>
        /// <param name="claimType">The claim type of interest.</param>
        /// <param name="claimValue">The claim value of interest.</param>
        /// <returns>A boolean indicating whether or not the identity has an associated claim.</returns>
        public static bool HasClaim(IPrincipal principal, string issuer, string claimType, string claimValue)
        {
            return ClaimProvider.HasClaim(principal as IClaimsPrincipal, issuer, claimType, claimValue);
        }

        public static bool HasClaim(IPrincipal principal, SPClaim claim)
        {
            return ClaimProvider.HasClaim(principal, claim.OriginalIssuer, claim.ClaimType, claim.Value);
        }

        /// <summary>
        /// Indicates whether or not a given principal has a particular claim value for a claim type.
        /// </summary>
        /// <param name="principal">The principal to test.</param>
        /// <param name="issuer">Identifies the original issuer of the claim.</param>
        /// <param name="claimType">The claim type of interest.</param>
        /// <param name="claimValue">The claim value of interest.</param>
        /// <returns>A boolean indicating whether or not the identity has an associated claim.</returns>
        public static bool HasClaim(IClaimsPrincipal principal, string issuer, string claimType, string claimValue)
        {
            return ClaimProvider.HasClaim(principal.Identity as IClaimsIdentity, issuer, claimType, claimValue);
        }

        /// <summary>
        /// Indicates whether or not a given identity has a particular claim value for a claim type.
        /// </summary>
        /// <param name="identity">The claims identity to test.</param>
        /// <param name="issuer">Identifies the original issuer of the claim.</param>
        /// <param name="claimType">The claim type of interest.</param>
        /// <param name="claimValue">The claim value of interest.</param>
        /// <returns>A boolean indicating whether or not the identity has an associated claim.</returns>
        public static bool HasClaim(IClaimsIdentity identity, string issuer, string claimType, string claimValue)
        {
            bool result = false;

            if (null != identity && !String.IsNullOrEmpty(issuer) && !String.IsNullOrEmpty(claimType) && !String.IsNullOrEmpty(claimValue))
            {
                string originalIssuer = SPOriginalIssuers.Format(SPOriginalIssuerType.ClaimProvider, issuer);

                result = identity.Claims.Exists((c) =>
                {
                    return (c.ClaimType.Equals(claimType, StringComparison.OrdinalIgnoreCase) &&
                        c.OriginalIssuer.Equals(originalIssuer, StringComparison.OrdinalIgnoreCase) &&
                        c.Value.Equals(claimValue, StringComparison.OrdinalIgnoreCase));
                }
                );
            }

            return result;
        }

        public static SPClaim CreateUserClaim(SecurityIdentifier sid)
        {
            return SPActiveDirectoryClaimProvider.CreateUserClaim(sid.Translate(typeof(NTAccount)).Value);
        }

        public static SPClaim CreateSecurityGroupClaim(SecurityIdentifier sid)
        {
            return SPActiveDirectoryClaimProvider.CreateSecurityGroupClaim(sid.Translate(typeof(NTAccount)).Value);
        }

        public static string GetDomainAccount(SPUser user)
        {
            return ClaimProvider.GetDomainAccount(user.LoginName);
        }

        public static string GetDomainAccount(string identityClaimValue)
        {
            return SPClaimProviderManager.Local.ConvertClaimToIdentifier(identityClaimValue);
        }

    }
}
